﻿using System;
using System.Runtime.InteropServices;

namespace MQRevo
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct SCARD_IO_REQUEST
    {
        public int dwProtocol;
        public int cbPciLength;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCARD_READERSTATE
    {
        // Pointer to the name of the reader being monitored.
        public string RdrName;

        // Not used by the smart card subsystem. This member is used by the application.
        public long UserData;

        // Current state of the reader, as seen by the application. This field can take on any of the following values, in combination, as a bit mask.
        public long RdrCurrState;

        // Current state of the reader, as known by the smart card resource manager. This field can take on any of the following values, in combination, as a bit mask.
        public long RdrEventState;

        // Number of bytes in the returned ATR.
        public long ATRLength;

        // ATR of the inserted card, with extra alignment bytes.
        public byte[] ATRValue;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct APDURec
    {
        public bool ForSend;

        // The T=0 instruction class.
        public byte bCLA;

        // An instruction code in the T=0 instruction class.
        public byte bINS;

        // Reference codes that complete the instruction code.
        public byte bP1;

        // Reference codes that complete the instruction code.
        public byte bP2;

        // The number of data bytes to be transmitted during the command, per ISO 7816-4, Section 8.2.1.
        public byte bP3;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public byte[] Data;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public byte[] SW;
    }

    internal static class MSSmartCard
    {
        #region General
        public const int SCARD_S_SUCCESS = 0;
        public const int SCARD_ATR_LENGTH = 33;
        #endregion

        #region Memory Card Type
        public const int CT_MCU = 0;
        // MCU 
        public const int CT_IIC_Auto = 1;
        // IIC (Auto Detect Memory Size) 
        public const int CT_IIC_1K = 2;
        // IIC (1K) 
        public const int CT_IIC_2K = 3;
        // IIC (2K) 
        public const int CT_IIC_4K = 4;
        // IIC (4K) 
        public const int CT_IIC_8K = 5;
        // IIC (8K) 
        public const int CT_IIC_16K = 6;
        // IIC (16K) 
        public const int CT_IIC_32K = 7;
        // IIC (32K) 
        public const int CT_IIC_64K = 8;
        // IIC (64K) 
        public const int CT_IIC_128K = 9;
        // IIC (128K) 
        public const int CT_IIC_256K = 10;
        // IIC (256K) 
        public const int CT_IIC_512K = 11;
        // IIC (512K) 
        public const int CT_IIC_1024K = 12;
        // IIC (1024K) 
        public const int CT_AT88SC153 = 13;
        // AT88SC153 
        public const int CT_AT88SC1608 = 14;
        // AT88SC1608 
        public const int CT_SLE4418 = 15;
        // SLE4418 
        public const int CT_SLE4428 = 16;
        // SLE4428 
        public const int CT_SLE4432 = 17;
        // SLE4432 
        public const int CT_SLE4442 = 18;
        // SLE4442 
        public const int CT_SLE4406 = 19;
        // SLE4406 
        public const int CT_SLE4436 = 20;
        // SLE4436 
        public const int CT_SLE5536 = 21;
        // SLE5536 
        public const int CT_MCUT0 = 22;
        // MCU T=0 
        public const int CT_MCUT1 = 23;
        // MCU T=1 
        public const int CT_MCU_Auto = 24;
        // MCU Autodetect 
        #endregion

        #region Context Scope
        /// <summary> 
        /// The context is a user context, and any database operations 
        /// are performed within the domain of the user. 
        /// </summary> 
        public const uint SCARD_SCOPE_USER = 0;

        /// <summary> 
        /// The context is that of the current terminal, and any database 
        /// operations are performed within the domain of that terminal. 
        /// (The calling application must have appropriate access permissions 
        /// for any database actions.) 
        /// </summary> 
        public const int SCARD_SCOPE_TERMINAL = 1;

        /// <summary> 
        /// The context is the system context, and any database operations 
        /// are performed within the domain of the system. (The calling 
        /// application must have appropriate access permissions for any 
        /// database actions.) 
        /// </summary> 
        public const int SCARD_SCOPE_SYSTEM = 2;

        /// <summary> 
        /// The application is unaware of the current state, and would like 
        /// to know. The use of this value results in an immediate return 
        /// from state transition monitoring services. This is represented 
        /// by all bits set to zero. 
        /// </summary> 
        public const int SCARD_STATE_UNAWARE = 0;

        /// <summary> 
        /// The application requested that this reader be ignored. No other 
        /// bits will be set. 
        /// </summary> 
        public const int SCARD_STATE_IGNORE = 1;

        /// <summary> 
        /// This implies that there is a difference between the state 
        /// believed by the application, and the state known by the Service 
        /// Manager.When this bit is set, the application may assume a 
        /// significant state change has occurred on this reader. 
        /// </summary> 
        public const int SCARD_STATE_CHANGED = 2;

        /// <summary> 
        /// This implies that the given reader name is not recognized by 
        /// the Service Manager. If this bit is set, then SCARD_STATE_CHANGED 
        /// and SCARD_STATE_IGNORE will also be set. 
        /// </summary> 
        public const int SCARD_STATE_UNKNOWN = 4;

        /// <summary> 
        /// This implies that the actual state of this reader is not 
        /// available. If this bit is set, then all the following bits are 
        /// clear. 
        /// </summary> 
        public const int SCARD_STATE_UNAVAILABLE = 8;

        /// <summary> 
        /// This implies that there is not card in the reader. If this bit 
        /// is set, all the following bits will be clear. 
        /// </summary> 
        public const int SCARD_STATE_EMPTY = 16;

        /// <summary> 
        /// This implies that there is a card in the reader. 
        /// </summary> 
        public const int SCARD_STATE_PRESENT = 32;

        /// <summary> 
        /// This implies that there is a card in the reader with an ATR 
        /// matching one of the target cards. If this bit is set, 
        /// SCARD_STATE_PRESENT will also be set. This bit is only returned 
        /// on the SCardLocateCard() service. 
        /// </summary> 
        public const int SCARD_STATE_ATRMATCH = 64;

        /// <summary> 
        /// This implies that the card in the reader is allocated for 
        /// exclusive use by another application. If this bit is set, 
        /// SCARD_STATE_PRESENT will also be set. 
        /// </summary> 
        public const int SCARD_STATE_EXCLUSIVE = 128;

        /// <summary> 
        /// This implies that the card in the reader is in use by one or 
        /// more other applications, but may be connected to in shared mode. 
        /// If this bit is set, SCARD_STATE_PRESENT will also be set. 
        /// </summary> 
        public const int SCARD_STATE_INUSE = 256;

        /// <summary> 
        /// This implies that the card in the reader is unresponsive or not 
        /// supported by the reader or software. 
        /// </summary> 
        public const int SCARD_STATE_MUTE = 512;

        /// <summary> 
        /// This implies that the card in the reader has not been powered up. 
        /// </summary> 
        public const int SCARD_STATE_UNPOWERED = 1024;

        /// <summary> 
        /// This application is not willing to share this card with other 
        /// applications. 
        /// </summary> 
        public const int SCARD_SHARE_EXCLUSIVE = 1;

        /// <summary> 
        /// This application is willing to share this card with other 
        /// applications. 
        /// </summary> 
        public const uint SCARD_SHARE_SHARED = 2;

        /// <summary> 
        /// This application demands direct control of the reader, so it 
        /// is not available to other applications. 
        /// </summary> 
        public const int SCARD_SHARE_DIRECT = 3;
        #endregion

        #region Disposition
        /// <summary> 
        /// Don't do anything special on close 
        /// </summary> 
        public const int SCARD_LEAVE_CARD = 0;

        /// <summary> 
        /// Reset the card on close 
        /// </summary> 
        public const int SCARD_RESET_CARD = 1;

        /// <summary> 
        /// Power down the card on close 
        /// </summary> 
        public const int SCARD_UNPOWER_CARD = 2;

        /// <summary> 
        /// Eject the card on close 
        /// </summary> 
        public const int SCARD_EJECT_CARD = 3;
        #endregion

        #region ACS IOCTL Class
        public const long FILE_DEVICE_SMARTCARD = 0x310000;

        // Reader action IOCTLs
        public const long IOCTL_SMARTCARD_DIRECT = FILE_DEVICE_SMARTCARD + 2050 * 4;
        public const long IOCTL_SMARTCARD_SELECT_SLOT = FILE_DEVICE_SMARTCARD + 2051 * 4;
        public const long IOCTL_SMARTCARD_DRAW_LCDBMP = FILE_DEVICE_SMARTCARD + 2052 * 4;
        public const long IOCTL_SMARTCARD_DISPLAY_LCD = FILE_DEVICE_SMARTCARD + 2053 * 4;
        public const long IOCTL_SMARTCARD_CLR_LCD = FILE_DEVICE_SMARTCARD + 2054 * 4;
        public const long IOCTL_SMARTCARD_READ_KEYPAD = FILE_DEVICE_SMARTCARD + 2055 * 4;
        public const long IOCTL_SMARTCARD_READ_RTC = FILE_DEVICE_SMARTCARD + 2057 * 4;
        public const long IOCTL_SMARTCARD_SET_RTC = FILE_DEVICE_SMARTCARD + 2058 * 4;
        public const long IOCTL_SMARTCARD_SET_OPTION = FILE_DEVICE_SMARTCARD + 2059 * 4;
        public const long IOCTL_SMARTCARD_SET_LED = FILE_DEVICE_SMARTCARD + 2060 * 4;
        public const long IOCTL_SMARTCARD_LOAD_KEY = FILE_DEVICE_SMARTCARD + 2062 * 4;
        public const long IOCTL_SMARTCARD_READ_EEPROM = FILE_DEVICE_SMARTCARD + 2065 * 4;
        public const long IOCTL_SMARTCARD_WRITE_EEPROM = FILE_DEVICE_SMARTCARD + 2066 * 4;
        public const long IOCTL_SMARTCARD_GET_VERSION = FILE_DEVICE_SMARTCARD + 2067 * 4;
        public const long IOCTL_SMARTCARD_GET_READER_INFO = FILE_DEVICE_SMARTCARD + 2051 * 4;
        public const long IOCTL_SMARTCARD_SET_CARD_TYPE = FILE_DEVICE_SMARTCARD + 2060 * 4;
        public const long IOCTL_SMARTCARD_ACR128_ESCAPE_COMMAND = FILE_DEVICE_SMARTCARD + 2079 * 4;
        #endregion

        #region Error Codes
        public const int SCARD_F_INTERNAL_ERROR = unchecked((int)0x80100001);
        public const int SCARD_E_CANCELLED = unchecked((int)0x80100002);
        public const int SCARD_E_INVALID_HANDLE = unchecked((int)0x80100003);
        public const int SCARD_E_INVALID_PARAMETER = unchecked((int)0x80100004);
        public const int SCARD_E_INVALID_TARGET = unchecked((int)0x80100005);
        public const int SCARD_E_NO_MEMORY = unchecked((int)0x80100006);
        public const int SCARD_F_WAITED_TOO_Integer = unchecked((int)0x80100007);
        public const int SCARD_E_INSUFFICIENT_BUFFER = unchecked((int)0x80100008);
        public const int SCARD_E_UNKNOWN_READER = unchecked((int)0x80100009);
        public const int SCARD_E_TIMEOUT = unchecked((int)0x8010000a);
        public const int SCARD_E_SHARING_VIOLATION = unchecked((int)0x8010000b);
        public const int SCARD_E_NO_SMARTCARD = unchecked((int)0x8010000c);
        public const int SCARD_E_UNKNOWN_CARD = unchecked((int)0x8010000d);
        public const int SCARD_E_CANT_DISPOSE = unchecked((int)0x8010000e);
        public const int SCARD_E_PROTO_MISMATCH = unchecked((int)0x8010000f);
        public const int SCARD_E_NOT_READY = unchecked((int)0x80100010);
        public const int SCARD_E_INVALID_VALUE = unchecked((int)0x80100011);
        public const int SCARD_E_SYSTEM_CANCELLED = unchecked((int)0x80100012);
        public const int SCARD_E_NO_READERS_AVAILABLE = unchecked((int)0x8010002e);
        public const int SCARD_F_COMM_ERROR = unchecked((int)0x80100013);
        public const int SCARD_F_UNKNOWN_ERROR = unchecked((int)0x80100014);
        public const int SCARD_E_INVALID_ATR = unchecked((int)0x80100015);
        public const int SCARD_E_NOT_TRANSACTED = unchecked((int)0x80100016);
        public const int SCARD_E_READER_UNAVAILABLE = unchecked((int)0x80100017);
        public const int SCARD_P_SHUTDOWN = unchecked((int)0x80100018);
        public const int SCARD_E_PCI_TOO_SMALL = unchecked((int)0x80100019);
        public const int SCARD_E_READER_UNSUPPORTED = unchecked((int)0x8010001a);
        public const int SCARD_E_DUPLICATE_READER = unchecked((int)0x8010001b);
        public const int SCARD_E_CARD_UNSUPPORTED = unchecked((int)0x8010001c);
        public const int SCARD_E_NO_SERVICE = unchecked((int)0x8010001d);
        public const int SCARD_E_SERVICE_STOPPED = unchecked((int)0x8010001e);
        public const int SCARD_E_DIR_NOT_FOUND = unchecked((int)0x80100023);
        public const int SCARD_W_UNSUPPORTED_CARD = unchecked((int)0x80100065);
        public const int SCARD_W_UNRESPONSIVE_CARD = unchecked((int)0x80100066);
        public const int SCARD_W_UNPOWERED_CARD = unchecked((int)0x80100067);
        public const int SCARD_W_RESET_CARD = unchecked((int)0x80100068);
        public const int SCARD_W_REMOVED_CARD = unchecked((int)0x80100069);
        #endregion

        #region Protocol
        /// <summary> 
        /// There is no active protocol. 
        /// </summary> 
        public const int SCARD_PROTOCOL_UNDEFINED = 0x0;

        /// <summary> 
        /// T=0 is the active protocol. 
        /// </summary> 
        public const uint SCARD_PROTOCOL_T0 = 0x1;

        /// <summary> 
        /// T=1 is the active protocol. 
        /// </summary> 
        public const uint SCARD_PROTOCOL_T1 = 0x2;

        /// <summary> 
        /// Raw is the active protocol. 
        /// </summary> 
        public const int SCARD_PROTOCOL_RAW = 0x10000;

        /// <summary>
        /// Use implicit PTS.
        /// </summary>
        public const int SCARD_PROTOCOL_DEFAULT = unchecked((int)0x80000000);
        #endregion

        #region Reader State
        /// <summary> 
        /// This value implies the driver is unaware of the current 
        /// state of the reader. 
        /// </summary> 
        public const int SCARD_UNKNOWN = 0;

        /// <summary> 
        /// This value implies there is no card in the reader. 
        /// </summary> 
        public const int SCARD_ABSENT = 1;

        /// <summary> 
        /// This value implies there is a card is present in the reader, 
        /// but that it has not been moved into position for use. 
        /// </summary> 
        public const int SCARD_PRESENT = 2;

        /// <summary> 
        /// This value implies there is a card in the reader in position 
        /// for use. The card is not powered. 
        /// </summary> 
        public const int SCARD_SWALLOWED = 3;

        /// <summary> 
        /// This value implies there is power is being provided to the card, 
        /// but the Reader Driver is unaware of the mode of the card. 
        /// </summary> 
        public const int SCARD_POWERED = 4;

        /// <summary> 
        /// This value implies the card has been reset and is awaiting 
        /// PTS negotiation. 
        /// </summary> 
        public const int SCARD_NEGOTIABLE = 5;

        /// <summary> 
        /// This value implies the card has been reset and specific 
        /// communication protocols have been established. 
        /// </summary> 
        public const int SCARD_SPECIFIC = 6;
        #endregion

        #region Prototypes
        [DllImport("WinScard.dll")]
        public static extern int SCardEstablishContext(uint dwScope, IntPtr notUsed1, IntPtr notUsed2, out IntPtr phContext);

        [DllImport("winscard.dll")]
        public static extern int SCardReleaseContext(IntPtr phContext);

        [DllImport("winscard.dll")]
        public static extern int SCardConnect(IntPtr hContext, string cReaderName, uint dwShareMode, uint dwPrefProtocol, ref IntPtr phCard, ref IntPtr ActiveProtocol);
        
        [DllImport("winscard.dll")]
        public static extern int SCardBeginTransaction(int hCard);

        [DllImport("winscard.dll")]
        public static extern int SCardDisconnect(IntPtr hCard, int Disposition);

        [DllImport("winscard.dll")]
        public static extern int SCardListReaderGroups(int hContext, ref string mzGroups, ref int pcchGroups);

        [DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
        public static extern int SCardListReaders(
          IntPtr hContext,
          byte[] mszGroups,
          byte[] mszReaders,
          ref UInt32 pcchReaders
          );


        [DllImport("winscard.dll")]
        public static extern int SCardStatus(IntPtr hCard, string szReaderName, ref uint pcchReaderLen, ref IntPtr State, ref IntPtr Protocol, ref byte ATR, ref uint ATRLen);

        [DllImport("winscard.dll")]
        public static extern int SCardEndTransaction(IntPtr hCard, int Disposition);

        [DllImport("winscard.dll")]
        public static extern int SCardState(IntPtr hCard, ref uint State, ref uint Protocol, ref byte ATR, ref uint ATRLen);

        [DllImport("winscard.dll")]
        public static extern int SCardTransmit(IntPtr hCard, ref SCARD_IO_REQUEST pioSendRequest, ref byte SendBuff, int SendBuffLen, ref SCARD_IO_REQUEST pioRecvRequest, ref byte RecvBuff, ref int RecvBuffLen);

       [DllImport("WinScard.dll", EntryPoint = "SCardGetStatusChangeA", CharSet = CharSet.Ansi)]
        public static extern uint SCardGetStatusChange(
            IntPtr hContext,
            uint dwTimeout,
            IntPtr rgReaderState,
            uint cReaders);

        #endregion

        #region Miscellaneous
        /// <summary> 
        /// Returns the specific error message.
        /// </summary> 
        /// <param name="errCode">The message represented by the error code.</param> 
        /// <returns></returns> 
        public static string GetScardErrMsg(long errCode)
        {
            switch (errCode)
            {
                case SCARD_E_CANCELLED:
                    return ("The action was canceled by an SCardCancel request.");
                case SCARD_E_CANT_DISPOSE:
                    return ("The system could not dispose of the media in the requested manner.");
                case SCARD_E_CARD_UNSUPPORTED:
                    return ("The smart card does not meet minimal requirements for support.");
                case SCARD_E_DUPLICATE_READER:
                    return ("The reader driver didn't produce a unique reader name.");
                case SCARD_E_INSUFFICIENT_BUFFER:
                    return ("The data buffer for returned data is too small for the returned data.");
                case SCARD_E_INVALID_ATR:
                    return ("An ATR string obtained from the registry is not a valid ATR string.");
                case SCARD_E_INVALID_HANDLE:
                    return ("The supplied handle was invalid.");
                case SCARD_E_INVALID_PARAMETER:
                    return ("One or more of the supplied parameters could not be properly interpreted.");
                case SCARD_E_INVALID_TARGET:
                    return ("Registry startup information is missing or invalid.");
                case SCARD_E_INVALID_VALUE:
                    return ("One or more of the supplied parameter values could not be properly interpreted.");
                case SCARD_E_NOT_READY:
                    return ("The reader or card is not ready to accept commands.");
                case SCARD_E_NOT_TRANSACTED:
                    return ("An attempt was made to end a non-existent transaction.");
                case SCARD_E_NO_MEMORY:
                    return ("Not enough memory available to complete this command.");
                case SCARD_E_NO_SERVICE:
                    return ("The smart card resource manager is not running.");
                case SCARD_E_NO_SMARTCARD:
                    return ("The operation requires a smart card, but no smart card is currently in the device.");
                case SCARD_E_PCI_TOO_SMALL:
                    return ("The PCI receive buffer was too small.");
                case SCARD_E_PROTO_MISMATCH:
                    return ("The requested protocols are incompatible with the protocol currently in use with the card.");
                case SCARD_E_READER_UNAVAILABLE:
                    return ("The specified reader is not currently available for use.");
                case SCARD_E_READER_UNSUPPORTED:
                    return ("The reader driver does not meet minimal requirements for support.");
                case SCARD_E_SERVICE_STOPPED:
                    return ("The smart card resource manager has shut down.");
                case SCARD_E_SHARING_VIOLATION:
                    return ("The smart card cannot be accessed because of other outstanding connections.");
                case SCARD_E_SYSTEM_CANCELLED:
                    return ("The action was canceled by the system, presumably to log off or shut down.");
                case SCARD_E_TIMEOUT:
                    return ("The user-specified timeout value has expired.");
                case SCARD_E_UNKNOWN_CARD:
                    return ("The specified smart card name is not recognized.");
                case SCARD_E_UNKNOWN_READER:
                    return ("The specified reader name is not recognized.");
                case SCARD_E_NO_READERS_AVAILABLE:
                    return ("No smart card reader is available.");
                case SCARD_F_COMM_ERROR:
                    return ("An internal communications error has been detected.");
                case SCARD_F_INTERNAL_ERROR:
                    return ("An internal consistency check failed.");
                case SCARD_F_UNKNOWN_ERROR:
                    return ("An internal error has been detected, but the source is unknown.");
                case SCARD_F_WAITED_TOO_Integer:
                    return ("An internal consistency timer has expired.");
                case SCARD_S_SUCCESS:
                    return ("No error was encountered.");
                case SCARD_E_DIR_NOT_FOUND:
                    return ("The identified directory does not exist in the smart card..");
                case SCARD_W_RESET_CARD:
                    return ("The smart card has been reset, so any shared state information is invalid.");
                case SCARD_W_UNPOWERED_CARD:
                    return ("Power has been removed from the smart card, so that further communication is not possible.");
                case SCARD_W_UNRESPONSIVE_CARD:
                    return ("The smart card is not responding to a reset.");
                case SCARD_W_UNSUPPORTED_CARD:
                    return ("The reader cannot communicate with the card, due to ATR string configuration conflicts.");
                case SCARD_W_REMOVED_CARD:
                    return ("The smart card has been removed, so further communication is not possible.");
                default:
                    return (String.Format("Code: {0}{1}Description: Undocumented error.", errCode, System.Environment.NewLine)); // !!! take note
            }
        }
        #endregion
    }
}
